#ifndef __SQL_UTIL__

#define __SQL_UTIL__

#include <mysql/mysql.h>
#include <sstream>
#include <utility>
#include "util.hpp"
#include "config.hpp"
#include "backUPInfo.hpp"

namespace cloud
{
    class SqlManager
    {
    private:
        static SqlManager *_instance;
        static std::mutex _mutex;
        std::string _file_table;
        std::string _content_table;
        MYSQL *_sql;

    private:
        SqlManager()
        {
            _sql = mysql_init(nullptr);
            // 连接数据库
            _sql = mysql_real_connect(_sql, Config::GetInstance()->GetSqlHost().c_str(),
                                      Config::GetInstance()->GetSqlUser().c_str(),
                                      Config::GetInstance()->GetSqlPassword().c_str(),
                                      Config::GetInstance()->GetSqlDataBase().c_str(),
                                      Config::GetInstance()->GetSqlPort(), nullptr, 0);
            // 设置编码
            mysql_set_character_set(_sql, "utf8");
            _file_table = Config::GetInstance()->GETFileTable();
            _content_table = Config::GetInstance()->GetContentTable();
        }

    public:
        static SqlManager *GetInstance()
        {
            if (_instance == nullptr)
            {
                _mutex.lock();
                if (_instance == nullptr)
                {
                    _instance = new SqlManager();
                }
                _mutex.unlock();
            }
            return _instance;
        }

        // 获取文件名,大小,上传时间
        void GetAllFileInfo(std::vector<string> &filename, std::vector<size_t> &filesize, std::vector<string> &time)
        {
            // 构建查询语句
            std::string tmp = "select file_name , size ,time from " + _file_table;
            int ret = mysql_query(_sql, tmp.c_str());
            // 获取查询结果
            MYSQL_RES *res = mysql_store_result(_sql);

            MYSQL_ROW row = mysql_fetch_row(res);
            while (row != nullptr)
            {
                // 表结构为 文件名,大小,上传时间
                filename.push_back(row[0]);
                filesize.push_back(atoi(row[1]));
                time.push_back(row[2]);
                row = mysql_fetch_row(res);
            }
            if (res != nullptr)
                mysql_free_result(res); // 释放内存
        }

        void GetFileContentByFileName(const std::string &filename, std::string &content)
        {
            std::string md5;
            GetMD5(filename,md5);
            GetFileContentByMD5(md5,content);
        }

        void GetMD5(const std::string &filename, std::string &md5)
        {
            std::string query1 = "select md5 from " + _file_table + " where file_name='" + filename + "'";
            int queryResult1 = mysql_query(_sql, query1.c_str());

            // 检查第一个查询是否执行成功
            if (queryResult1 != 0)
            {
                Log::getLogger("globalLogger")->error("Failed to execute query to get MD5 for file %s: %s", filename.c_str(), mysql_error(_sql));
                return;
            }

            MYSQL_RES *res = mysql_store_result(_sql);

            // 检查是否成功获取结果
            if (res == nullptr)
            {
                Log::getLogger("globalLogger")->error("Failed to get result for query to get MD5 for file %s: %s", filename.c_str(), mysql_error(_sql));
                mysql_free_result(res);
                return;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            if (row != nullptr)
            {
                md5=row[0];
            }
            else
            {
                Log::getLogger("globalLogger")->warn("获取文件%s 内容失败", filename.c_str());
            }
            mysql_free_result(res);
        }

        void GetFileContentByMD5(const std::string &md5, std::string &content)
        {
            std::string query2 = "select content from " + _content_table + " where md5='" + md5 + "'";
            int queryResult = mysql_query(_sql, query2.c_str());

            // 检查第二个查询是否执行成功
            if (queryResult != 0)
            {
                Log::getLogger("globalLogger")->error("Failed to execute query to get content for MD5 %s: %s", md5.c_str(), mysql_error(_sql));
                return;
            }

            MYSQL_RES *res = mysql_store_result(_sql);

            // 检查是否成功获取结果
            if (res == nullptr)
            {
                Log::getLogger("globalLogger")->error("Failed to get result for query to get content for MD5 %s: %s", md5.c_str(), mysql_error(_sql));
                mysql_free_result(res);
                return;
            }

            MYSQL_ROW row1 = mysql_fetch_row(res);
            if (row1 != nullptr)
            {
                // BundleUtil::UnCompress(row1[0], content);
                unsigned long *lengths = mysql_fetch_lengths(res);
                content.assign(row1[0], lengths[0]);
            }

            mysql_free_result(res);
        }

        void InsertContent(const BackupInfo &val)
        {
            char tmp[512];
            sprintf(tmp, "insert into %s (md5, content) \
            values( '%s',load_file('%s'))",
                    _content_table.c_str(),
                    val._md5.c_str(),
                    val._pack_path.c_str());
            int ret = mysql_query(_sql, tmp);
            if (ret != 0)
                Log::getLogger("globalLogger")->warn("插入文件%s 失败", val._packname.c_str());
            else
                Log::getLogger("globalLogger")->warn("插入文件%s 成功", val._packname.c_str());
        }

        void InsertFile(const std::string filename, const std::string md5, const size_t size)
        {
            char tmp[512];
            sprintf(tmp, "insert into %s (file_name, size,md5,time) \
            values( '%s',%zu,'%s', now())",
                    _file_table.c_str(),
                    filename.c_str(),
                    size,
                    md5.c_str());
            int ret = mysql_query(_sql, tmp);
            if (ret != 0)
                Log::getLogger("globalLogger")->warn("插入文件%s 失败", filename.c_str());
            else
                Log::getLogger("globalLogger")->warn("插入文件%s 成功", filename.c_str());
        }

        bool hasfile(std::string &md5)
        {
            // 构建查询语句
            std::string tmp = "select md5 from " + _content_table + " where md5='" + md5.c_str() + "'";

            if (mysql_query(_sql, tmp.c_str()) != 0)
            { // 检查查询是否成功
                std::cerr << "Query execution failed: " << mysql_error(_sql) << std::endl;
                return false; // 查询失败，直接返回
            }

            // 获取查询结果
            MYSQL_RES *res = mysql_store_result(_sql); // 修正为使用有效的连接对象

            if (res == nullptr)
            { // 检查结果是否获取成功
                std::cerr << "Failed to get query result: " << mysql_error(_sql) << std::endl;
                return false; // 获取结果失败，返回
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            bool exists = (row != nullptr);

            mysql_free_result(res); // 无论结果如何，都释放结果资源

            return exists;
        }
    };

    std::mutex SqlManager::_mutex;
    SqlManager *SqlManager::_instance = nullptr;
}

#endif //__SQL_UTIL__